//
// Created on 2025/7/19.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#ifndef STREAMCORE_ASYNC_TASK_QUEUE_H
#define STREAMCORE_ASYNC_TASK_QUEUE_H

#include <functional>
#include <mutex>
#include <napi/native_api.h>


class AsyncTaskQueue {
public:
    using Task = std::function<void(napi_env)>;

    static void Initialize(napi_env env) {
        std::call_once(init_flag_, [env] {
            napi_value resource_name;
            napi_create_string_utf8(env, "AsyncTaskQueue", NAPI_AUTO_LENGTH, &resource_name);
            napi_status status = napi_create_threadsafe_function(env, nullptr, nullptr, resource_name, 0, 1, nullptr,
                                                                 nullptr, nullptr, TaskCallback, &tsfn_);

            if (status != napi_ok) {
                throw std::runtime_error(ERR_INIT_ASYNC_TASK_QUEUE_FAILED);
            }
        });
    }

    template <typename F>
    static void Enqueue(F &&task, napi_threadsafe_function_call_mode mode = napi_tsfn_nonblocking) {
        auto task_wrapper = new TaskWrap<F>(std::forward<F>(task));

        napi_acquire_threadsafe_function(tsfn_);

        napi_call_threadsafe_function(tsfn_, task_wrapper, mode);
    }

    static void RegisterCleanupHook(napi_env env) {
        napi_add_env_cleanup_hook(
            env, [](void *data) { AsyncTaskQueue::Cleanup(); }, nullptr);
    }


private:
    static constexpr auto ERR_INIT_ASYNC_TASK_QUEUE_FAILED = "initialize AsyncTaskQueue failed";
    struct TaskBase {
        virtual ~TaskBase() = default;
        virtual void Execute(napi_env env) = 0;
    };

    template <typename F> struct TaskWrap : TaskBase {
        F task;

        TaskWrap(F &&f) : task(std::forward<F>(f)) {}

        void Execute(napi_env env) override { task(env); }
    };

    static void Cleanup() {
        if (tsfn_) {
            napi_release_threadsafe_function(tsfn_, napi_tsfn_release);
            tsfn_ = nullptr;
        }
    }

    static void TaskCallback(napi_env env, napi_value js_callback, void *context, void *data) {
        (void)js_callback;
        (void)context;

        auto task = static_cast<TaskBase *>(data);
        if (task) {
            task->Execute(env);
            delete task;

            napi_release_threadsafe_function(tsfn_, napi_tsfn_release);
        }
    }

    static std::once_flag init_flag_;
    static napi_threadsafe_function tsfn_;
};

#endif // STREAMCORE_ASYNC_TASK_QUEUE_H
